package com.yxinsur.imc.business.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yxinsur.imc.business.domain.ImcContractExpenseExpression;
import com.yxinsur.imc.business.domain.ImcContractVariable;
import com.yxinsur.imc.business.domain.ImcContractVariableStrategy;
import com.yxinsur.imc.business.domain.ImcRecn;
import com.yxinsur.imc.business.enums.ExpenseExpressionMsgTypeEnum;
import com.yxinsur.imc.business.excel.RecnTemplate;
import com.yxinsur.imc.business.mapper.ImcContractExpenseExpressionMapper;
import com.yxinsur.imc.business.mapper.ImcContractExpenseMapper;
import com.yxinsur.imc.business.mapper.ImcRecnMapper;
import com.yxinsur.imc.business.service.IImcRecnExpenseService;
import com.yxinsur.imc.business.vo.ContractExpenseVo;
import com.yxinsur.imc.business.vo.ContractVo;
import com.yxinsur.imc.business.vo.RecnDetailsVo;
import com.yxinsur.imc.common.annotation.Excel;
import com.yxinsur.imc.common.utils.SecurityUtils;
import com.yxinsur.imc.common.utils.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 手续费对账单-协议费用科目 Service业务层处理
 *
 * @author imc
 * @date 2021-09-15
 */
@Service
public class ImcRecnExpenseServiceImpl extends ServiceImpl<ImcRecnMapper, ImcRecn> implements IImcRecnExpenseService {

    private static final Logger logger = LoggerFactory.getLogger(ImcRecnExpenseServiceImpl.class);
    @Autowired
    private ImcRecnMapper imcRecnMapper;
    @Autowired
    private ImcContractExpenseMapper imcContractExpenseMapper;
    @Autowired
    private ImcContractExpenseExpressionMapper imcContractExpenseExpressionMapper;

    /**
     * 根据{选择月度}、{供应商id}查询协议
     *
     * @param settleMonth 选择月度
     * @param supplierId  供应商id
     * @return 协议
     */
    @Override
    public List<ContractVo> listContractBySettleMonthAndSupplierId(String settleMonth, Long supplierId) {
        return this.imcContractExpenseMapper.listImcContractBySettleMonthAndSupplierId(settleMonth, supplierId, SecurityUtils.getTenantId());
    }

    /**
     * 根据{协议id}查询协议费用科目
     *
     * @param contractIdList 协议id集合
     * @param expenseType    协议费用科目类型
     * @return 协议费用科目
     */
    @Override
    public List<ContractExpenseVo> listImcContractExpenseByContractIds(List<Long> contractIdList, String expenseType) {
        return this.imcContractExpenseMapper.listImcContractExpenseByContractIds(contractIdList, expenseType, SecurityUtils.getTenantId());
    }

    /**
     * 根据{主表id}查询协议费用科目
     *
     * @param recnId 主表id
     * @return 协议费用科目
     */
    @Override
    public List<ContractExpenseVo> listImcContractExpenseByRecnId(Long recnId) {
        ImcRecn imcRecn = this.imcRecnMapper.getImcRecnById(recnId);
        if (ObjectUtil.isEmpty(imcRecn)) {
            return CollUtil.newArrayList();
        }
        List<String> contractExpenseStrIds = CollUtil.newArrayList(StrUtil.split(imcRecn.getContractExpenseIds(), ","));
        List<Long> contractExpenseIds = contractExpenseStrIds.stream().map(Long::parseLong).collect(Collectors.toList());
        return this.imcContractExpenseMapper.listContractExpenseByIds(contractExpenseIds, SecurityUtils.getTenantId());
    }

    /**
     * 根据{选择月度}{协议费用科目ID}生成查询SQL语句
     *
     * @param settleMonth       选择月度
     * @param contractExpenseId 协议费用科目ID
     * @return 查询SQL语句
     */
    @Override
    public String getCriteriaSQL(String settleMonth, Long contractExpenseId) {
        StringBuilder criteriaSQL = new StringBuilder();
        List<ImcContractExpenseExpression> expenseExpressionList = this.imcContractExpenseMapper.listImcContractExpenseExpressionByContractExpenseId(contractExpenseId);
        if (CollUtil.isNotEmpty(expenseExpressionList)) {
            for (ImcContractExpenseExpression item : expenseExpressionList) {
                // a.解析：口径字段（字段名称）：如果不为括号则转换为 ior.字段名
                ImcContractVariable contractVariable = this.imcContractExpenseExpressionMapper.selectImcContractVariableById(item.getVariableId());
                if ("left_bracket".equals(contractVariable.getVariableName()) || "right_bracket".equals(contractVariable.getVariableName())) {
                    criteriaSQL.append(contractVariable.getVariableLabel());
                } else {
                    criteriaSQL.append("ior.").append(contractVariable.getVariableName());
                }

                // b.解析：运算符
                criteriaSQL.append(" ").append(transOperator(item.getOperator(), ExpenseExpressionMsgTypeEnum.SQL.name()));

                // c.解析：口径字段（字段数值）&处理策略
                if (StrUtil.isNotBlank(item.getVariableValue())) {
                    if (ObjectUtil.isEmpty(item.getStrategyId())) {
                        criteriaSQL.append(" ").append("'").append(item.getVariableValue()).append("'");
                    } else {
                        ImcContractVariableStrategy strategy = this.imcContractExpenseExpressionMapper.selectImcContractVariableStrategyById(item.getStrategyId());
                        // day（variable_value的值）为结算年月后的具体的日期
                        String day = item.getVariableValue().length() > 1 ? item.getVariableValue() : "0" + item.getVariableValue();
                        // 替换结算月度为手续费结算月度（YYYY-MM-DD YYYY-MM为结算月 DD为variable_value的值）
                        if ("current_settlement_month".equals(strategy.getStrategyCode())) {
                            criteriaSQL.append(" ").append("'").append(DateUtil.format(DateUtil.parse(settleMonth, "yyyy-MM"), "yyyy-MM")).append("-").append(day).append("'");
                        }
                        // 替换结算月度为手续费结算月度（YYYY-MM-DD YYYY-MM为结算月+1月 DD为variable_value的值）
                        if ("next_settlement_month".equals(strategy.getStrategyCode())) {
                            criteriaSQL.append(" ").append("'").append(DateUtil.format(org.apache.commons.lang3.time.DateUtils.addMonths(DateUtil.parse(settleMonth, "yyyy-MM"), 1), "yyyy-MM")).append("-").append(day).append("'");
                        }
                        // 具体时间，选举时间选择器的YYYY-MM-DD的00:00:00
                        if ("determine_datetime".equals(strategy.getStrategyCode())) {
                            criteriaSQL.append(" ").append("'").append(DateUtil.format(DateUtil.parseDate(item.getVariableValue()),"yyyy-MM-dd HH:mm:ss")).append("'");
                        }
                    }
                }
                // d.逻辑运算符
                if (StrUtil.isNotBlank(item.getLogicalOperator()) && !"0".equals(item.getLogicalOperator())) {
                    criteriaSQL.append(" ").append(transLogicalOperator(item.getLogicalOperator(), "SQL")).append(" ");
                }
                criteriaSQL.append(" ");
            }
        }
        logger.debug("协议费用科目表达式拼接SQL：[ " + criteriaSQL + " ]");
        return criteriaSQL.toString();
    }

    /**
     * 转换：按照{orderRiskId}行转列
     *
     * @param recnDetailsVoList 初始的手续费核算结果集合
     * @return 行转列后的手续费核算结果集合
     */
    @Override
    public List<RecnDetailsVo> rowTransColumnByOrderRiskId(List<RecnDetailsVo> recnDetailsVoList) {
        return recnDetailsVoList.stream()
                .collect(Collectors.groupingBy(RecnDetailsVo::getOrderRiskId))
                .entrySet()
                .stream()
                .map(entry -> {
                    Long key = entry.getKey();
                    RecnDetailsVo checkItemVo = new RecnDetailsVo();
                    BeanUtils.copyProperties(entry.getValue().get(0), checkItemVo);
                    checkItemVo.setOrderRiskId(key);
                    return checkItemVo;
                }).collect(Collectors.toList());
    }

    /**
     * 导出：设置表头
     *
     * @param contractExpenseIdList 协议费用科目ID集合（标题）
     * @return Map<String, String>
     */
    @Override
    public Map<String, String> setTitle(List<Long> contractExpenseIdList) {
        List<Map<String, String>> resultList = CollUtil.newArrayList();
        String expenseNameA = contractExpenseIdList.size() > 0 ? this.imcContractExpenseMapper.getExpenseNameById(contractExpenseIdList.get(0)) : null;
        if (StrUtil.isNotEmpty(expenseNameA)) {
            Map<String, String> profit1Map = CollUtil.newHashMap();
            profit1Map.put("titleText", expenseNameA);
            profit1Map.put("title", "contractExpenseA");
            resultList.add(profit1Map);
        }

        String expenseNameB = contractExpenseIdList.size() > 1 ? this.imcContractExpenseMapper.getExpenseNameById(contractExpenseIdList.get(1)) : null;
        if (StrUtil.isNotEmpty(expenseNameB)) {
            Map<String, String> profit2Map = CollUtil.newHashMap();
            profit2Map.put("titleText", expenseNameB);
            profit2Map.put("title", "contractExpenseB");
            resultList.add(profit2Map);
        }

        String expenseNameC = contractExpenseIdList.size() > 2 ? this.imcContractExpenseMapper.getExpenseNameById(contractExpenseIdList.get(2)) : null;
        if (StrUtil.isNotEmpty(expenseNameC)) {
            Map<String, String> profit3Map = CollUtil.newHashMap();
            profit3Map.put("titleText", expenseNameC);
            profit3Map.put("title", "contractExpenseC");
            resultList.add(profit3Map);
        }

        String expenseNameD = contractExpenseIdList.size() > 3 ? this.imcContractExpenseMapper.getExpenseNameById(contractExpenseIdList.get(3)) : null;
        if (StrUtil.isNotEmpty(expenseNameD)) {
            Map<String, String> profit4Map = CollUtil.newHashMap();
            profit4Map.put("titleText", expenseNameD);
            profit4Map.put("title", "contractExpenseD");
            resultList.add(profit4Map);
        }

        String expenseNameE = contractExpenseIdList.size() > 4 ? this.imcContractExpenseMapper.getExpenseNameById(contractExpenseIdList.get(4)) : null;
        if (StrUtil.isNotEmpty(expenseNameE)) {
            Map<String, String> profit5Map = CollUtil.newHashMap();
            profit5Map.put("titleText", expenseNameE);
            profit5Map.put("title", "contractExpenseE");
            resultList.add(profit5Map);
        }

        Map<String, String> resultMap = CollUtil.newHashMap();
        for (Map<String, String> title : resultList) {
            String key = title.get("title");
            String value = title.get("titleText");
            resultMap.put(key, value);
        }
        return resultMap;
    }

    /**
     * 导出：设置动态列标题的注解
     *
     * @param titles 动态列标题
     * @throws NoSuchFieldException   NoSuchFieldException
     * @throws IllegalAccessException IllegalAccessException
     */
    @Override
    public void setCommDetailsAnnotation(Map<String, String> titles) throws NoSuchFieldException, IllegalAccessException {
        Field fieldA = RecnTemplate.class.getDeclaredField("contractExpenseA");
        Field fieldB = RecnTemplate.class.getDeclaredField("contractExpenseB");
        Field fieldC = RecnTemplate.class.getDeclaredField("contractExpenseC");
        Field fieldD = RecnTemplate.class.getDeclaredField("contractExpenseD");
        Field fieldE = RecnTemplate.class.getDeclaredField("contractExpenseE");
        Field fieldF = RecnTemplate.class.getDeclaredField("contractExpenseF");
        Field fieldG = RecnTemplate.class.getDeclaredField("contractExpenseG");
        Field[] fields = new Field[]{fieldA, fieldB, fieldC, fieldD, fieldE, fieldF, fieldG};
        for (Field field : fields) {
            String value = titles.get(field.getName());
            Excel excel = field.getAnnotation(Excel.class);
            InvocationHandler h = Proxy.getInvocationHandler(excel);
            Field hField = h.getClass().getDeclaredField("memberValues");
            hField.setAccessible(true);
            Map<String, Object> memberValues = (Map<String, Object>) hField.get(h);
            if (StringUtils.isEmpty(value)) {
                memberValues.put("type", Excel.Type.IMPORT);
            } else {
                memberValues.put("name", value);
            }
            hField.setAccessible(false);
        }
    }

    /**
     * 校验：上传文件模板校验是否为正确模板
     *
     * @param file        上传文件
     * @param titleStrArr title数组
     * @param sheetName   sheetName
     * @return boolean 是否为正确模板
     * @throws IOException IOException
     */
    @Override
    public boolean importTemplateVerify(MultipartFile file, String[] titleStrArr, String sheetName) throws IOException {
        Workbook wb = WorkbookFactory.create(file.getInputStream());
        Sheet sheet = wb.getSheet(sheetName);
        Row titleRow = sheet.getRow(0);
        int cellNum = titleRow.getLastCellNum();
        for (int i = 0; i < cellNum; i++) {
            String titleStr = new DataFormatter().formatCellValue(titleRow.getCell(i));
            boolean isRightTemplate = titleStrArr[i].equals(titleStr);
            logger.info("标题：[" + titleStr + "-" + titleStrArr[i] + "]");
            if (!isRightTemplate) {
                return false;
            }
        }
        return true;
    }

    /**
     * 转换：运算符
     *
     * @param operator 运算符
     * @param msgType  生成/校验表达式返回前端消息类型
     * @return 转换后的运算符
     */
    @Override
    public String transOperator(String operator, String msgType) {
        if (StrUtil.isBlank(operator)) {
            return "";
        }
        if (ExpenseExpressionMsgTypeEnum.SQL.name().equals(msgType)) {
            switch (operator) {
                case "greater":
                    return ">";
                case "less":
                    return "<";
                case "eq":
                    return "=";
                case "greaterEq":
                    return ">=";
                case "lessEq":
                    return "<=";
                case "nEq":
                    return "<>";
                default:
                    return "";
            }
        }
        if (ExpenseExpressionMsgTypeEnum.TAG.name().equals(msgType)) {
            switch (operator) {
                case "greater":
                    return "大于";
                case "less":
                    return "小于";
                case "eq":
                    return "等于";
                case "greaterEq":
                    return "大于等于";
                case "lessEq":
                    return "小于等于";
                case "nEq":
                    return "不等于";
                default:
                    return "";
            }
        }
        return "";
    }

    /**
     * 转换：逻辑运算符
     *
     * @param logicalOperator 逻辑运算符
     * @param msgType         生成/校验表达式返回前端消息类型
     * @return 转换后的逻辑运算符
     */
    @Override
    public String transLogicalOperator(String logicalOperator, String msgType) {
        if (StrUtil.isBlank(logicalOperator)) {
            return "";
        }
        if (ExpenseExpressionMsgTypeEnum.SQL.name().equals(msgType)) {
            switch (logicalOperator) {
                case "1":
                    return "AND";
                case "2":
                    return "OR";
                default:
                    return "";
            }
        }
        if (ExpenseExpressionMsgTypeEnum.TAG.name().equals(msgType)) {
            switch (logicalOperator) {
                case "1":
                    return "且";
                case "2":
                    return "或";
                default:
                    return "";
            }
        }
        return "";
    }
}
